/**
  ******************************************************************************
  * @file   app_fee_strategy.c
  * @author leven
  ******************************************************************************
  */

#include "../inc/app_fee_strategy.h"

#include <string.h>
#if (0x01 == FEE_STRATEGY_DEBUG)
#include <stdio.h>
#endif

/** 费率 */
typedef struct {
    uint32_t   price_elec[FEE_STRATEGY_PRICE_TPYE_MAX];      /* 尖、峰、平、谷对应的电费价格 */
    uint32_t   price_service[FEE_STRATEGY_PRICE_TPYE_MAX];   /* 尖、峰、平、谷对应的服务费价格 */
    uint32_t   period_elec[FEE_STRATEGY_PERIOD_TPYE_MAX];    /* 每个时段的电费价格对应尖、峰、平、谷中的某一种 */
    uint32_t   period_service[FEE_STRATEGY_PERIOD_TPYE_MAX]; /* 每个时段的服务费价格对应尖、峰、平、谷中的某一种 */
} fee_strategy_t;

/** 计费策略信息 */
typedef struct {
    uint64_t   start_time;          /* 开始充电时间 */
    uint64_t   end_time;            /* 结束充电时间 */
    uint32_t   total_time;          /* 充电总时间 */

    uint32_t   start_elect;         /* 开始电量 */
    uint32_t   end_elect;           /* 结束电量 */
    uint32_t   total_elect;         /* 充电总电量 */

    uint32_t   total_elec_fee;      /* 充电总费用 */
    uint32_t   total_service_fee;   /* 服务总费用 */

    uint32_t   price_elec_fee[FEE_STRATEGY_PRICE_TPYE_MAX];      /* 尖、峰、平、谷对应的电费 */
    uint32_t   price_service_fee[FEE_STRATEGY_PRICE_TPYE_MAX];   /* 尖、峰、平、谷对应的服务费 */
    uint32_t   price_elect[FEE_STRATEGY_PRICE_TPYE_MAX];         /* 尖、峰、平、谷对应的电量 */

    uint32_t   period_elect[FEE_STRATEGY_PERIOD_TPYE_MAX];       /* 每个时段的电量 */

    fee_strategy_t fee_strategy;
} fee_info_t;

static fee_info_t s_fee_info[FEE_MAX_PORT_NUM];

uint64_t get_fi_start_time(enum fee_number no)
{
    return s_fee_info[no].start_time;
}
uint64_t get_fi_end_time(enum fee_number no)
{
    return s_fee_info[no].end_time;
}
uint64_t get_fi_total_time(enum fee_number no)
{
    return s_fee_info[no].total_time;
}

uint32_t get_fi_start_elect(enum fee_number no)
{
    return s_fee_info[no].start_elect;
}
uint32_t get_fi_end_elect(enum fee_number no)
{
    return s_fee_info[no].end_elect;
}
uint32_t get_fi_total_elect(enum fee_number no)
{
    return s_fee_info[no].total_elect;
}

uint32_t get_fi_total_elec_fee(enum fee_number no)
{
    return s_fee_info[no].total_elec_fee / 100; /* 此处数据缩小100倍 */
}
uint32_t get_fi_total_service_fee(enum fee_number no)
{
    return s_fee_info[no].total_service_fee / 100; /* 此处数据缩小100倍 */
}

uint32_t get_fi_price_elec_fee(enum fee_number no, enum fee_strategy_price_type type)
{
    if (FEE_STRATEGY_PRICE_TPYE_MAX <= type) {
        return FEE_STRATEGY_ERROR;
    }

    return s_fee_info[no].price_elec_fee[type] / 100; /* 此处数据缩小100倍 */
}
uint32_t get_fi_price_service_fee(enum fee_number no, enum fee_strategy_price_type type)
{
    if (FEE_STRATEGY_PRICE_TPYE_MAX <= type) {
        return FEE_STRATEGY_ERROR;
    }

    return s_fee_info[no].price_service_fee[type] / 100; /* 此处数据缩小100倍 */
}
uint32_t get_fi_price_elect(enum fee_number no, enum fee_strategy_price_type type)
{
    if (FEE_STRATEGY_PRICE_TPYE_MAX <= type) {
        return FEE_STRATEGY_ERROR;
    }

    return s_fee_info[no].price_elect[type];
}

uint32_t get_fi_period_elect(enum fee_number no, uint8_t period)
{
    if (FEE_STRATEGY_PERIOD_TPYE_MAX <= period) {
        return FEE_STRATEGY_ERROR;
    }

    return s_fee_info[no].period_elect[period];
}

void set_fi_price_elec(enum fee_number no, enum fee_strategy_price_type type, uint32_t fee)
{
    if (FEE_STRATEGY_PRICE_TPYE_MAX <= type) {
        return;
    }

    s_fee_info[no].fee_strategy.price_elec[type] = fee;
}
void set_fi_price_service(enum fee_number no, enum fee_strategy_price_type type, uint32_t fee)
{
    if (FEE_STRATEGY_PRICE_TPYE_MAX <= type) {
        return;
    }

    s_fee_info[no].fee_strategy.price_service[type] = fee;
}

uint32_t get_fi_price_elec(enum fee_number no, enum fee_strategy_price_type type)
{
    if (FEE_STRATEGY_PRICE_TPYE_MAX <= type) {
        return FEE_STRATEGY_ERROR;
    }

    return s_fee_info[no].fee_strategy.price_elec[type];
}
uint32_t get_fi_price_service(enum fee_number no, enum fee_strategy_price_type type)
{
    if (FEE_STRATEGY_PRICE_TPYE_MAX <= type) {
        return FEE_STRATEGY_ERROR;
    }

    return s_fee_info[no].fee_strategy.price_service[type];
}

void set_fi_period_elec_type(enum fee_number no, uint8_t period, enum fee_strategy_price_type type)
{
    if (FEE_STRATEGY_PERIOD_TPYE_MAX <= period) {
        return;
    }
    if (FEE_STRATEGY_PRICE_TPYE_MAX <= type) {
        return;
    }

    s_fee_info[no].fee_strategy.period_elec[period] = type;
}
void set_fi_period_service_type(enum fee_number no, uint8_t period, enum fee_strategy_price_type type)
{
    if (FEE_STRATEGY_PERIOD_TPYE_MAX <= period) {
        return;
    }
    if (FEE_STRATEGY_PRICE_TPYE_MAX <= type) {
        return;
    }

    s_fee_info[no].fee_strategy.period_service[period] = type;
}

void fi_init(enum fee_number no, uint64_t curr_time, uint32_t curr_elect)
{
    s_fee_info[no].start_time = curr_time;
    s_fee_info[no].end_time = s_fee_info[no].start_time;
    s_fee_info[no].total_time = s_fee_info[no].end_time - s_fee_info[no].start_time;

    s_fee_info[no].start_elect = curr_elect * 10; /* 该值本身就有1000倍的放大，再乘以10就相当于被放大了10000倍（精确到小数点后4位） */
    s_fee_info[no].end_elect = s_fee_info[no].start_elect;
    s_fee_info[no].total_elect = s_fee_info[no].end_elect - s_fee_info[no].start_elect;

    s_fee_info[no].total_elec_fee = 0;
    s_fee_info[no].total_service_fee = 0;

    memset(s_fee_info[no].price_elec_fee, 0x00, sizeof(s_fee_info[no].price_elec_fee));
    memset(s_fee_info[no].price_service_fee, 0x00, sizeof(s_fee_info[no].price_service_fee));
    memset(s_fee_info[no].price_elect, 0x00, sizeof(s_fee_info[no].price_elect));

    memset(s_fee_info[no].period_elect, 0x00, sizeof(s_fee_info[no].period_elect));
}

/**
 * @brief 获取当前时段
 * @return 当前时间（单位：秒）
 */
static uint8_t get_period(uint64_t curr_time)
{
#define BEIJING_TIME_ZONE   ( 8 * 60 * 60)
#define SECONDS_OF_DAY      (24 * 60 * 60)

    uint8_t period;
    uint32_t seconds_of_today;

    seconds_of_today = (curr_time + BEIJING_TIME_ZONE) % SECONDS_OF_DAY;
    period = seconds_of_today / (24 * 60 / FEE_STRATEGY_PERIOD_TPYE_MAX * 60);

    return period;
}

/**
 * @brief 获取对应时段的费率价格
 * @param curr_time 当前时间（单位：秒）
 * @param elec_price 电费
 * @param service_price 服务费
 */
static void get_price(enum fee_number no, uint64_t curr_time, uint32_t *elec_price, uint32_t *service_price)
{
    uint8_t curr_period;
    enum fee_strategy_price_type curr_price;

    curr_period = get_period(curr_time);

    if (FEE_STRATEGY_PERIOD_TPYE_MAX <= curr_period) {
        return;
    }

    curr_price = s_fee_info[no].fee_strategy.period_elec[curr_period];
    if (FEE_STRATEGY_PRICE_TPYE_MAX > curr_price) {
        *elec_price = s_fee_info[no].fee_strategy.price_elec[curr_price];
    }

    curr_price = s_fee_info[no].fee_strategy.period_service[curr_period];
    if (FEE_STRATEGY_PRICE_TPYE_MAX > curr_price) {
        *service_price = s_fee_info[no].fee_strategy.price_service[curr_price];
    }
}

void fi_calculate(enum fee_number no, uint64_t curr_time, uint32_t curr_elect)
{
#if (0x01 == FEE_STRATEGY_DEBUG)
    static uint8_t count = 0;
#endif

    uint64_t _curr_time = 0;
    uint32_t _curr_elec_fee = 0, _curr_service_fee = 0, _curr_elect = 0;
    uint32_t _diff_elect = 0, _diff_elec_fee = 0, _diff_service_fee = 0;

    uint8_t _curr_period;
    enum fee_strategy_price_type _curr_price_elec, _curr_price_service;

    _curr_time = curr_time;

    _curr_period = get_period(_curr_time);
    get_price(no, _curr_time, &_curr_elec_fee, &_curr_service_fee);

    _curr_elect = curr_elect * 10; /* 该值本身就有1000倍的放大，再乘以10就相当于被放大了10000倍（精确到小数点后4位） */

    /* 计算增量电量 */
    _diff_elect = _curr_elect - s_fee_info[no].total_elect - s_fee_info[no].start_elect;
    /* 计算增量电费 */
    _diff_elec_fee = _diff_elect * _curr_elec_fee / 100; /* 此处数值被缩小了100倍，剩余的100倍在数据返回时缩小 */
    /* 计算增量服务费 */
    _diff_service_fee = _diff_elect * _curr_service_fee / 100; /* 此处数值被缩小了100倍，剩余的100倍在数据返回时缩小 */

    s_fee_info[no].end_time = _curr_time;
    if (s_fee_info[no].end_time >= s_fee_info[no].start_time) {
        s_fee_info[no].total_time = s_fee_info[no].end_time - s_fee_info[no].start_time;
    }

    s_fee_info[no].end_elect = _curr_elect;
    if (s_fee_info[no].end_elect >= s_fee_info[no].start_elect) {
        s_fee_info[no].total_elect = s_fee_info[no].end_elect - s_fee_info[no].start_elect;
    }

    s_fee_info[no].total_elec_fee += _diff_elec_fee;
    s_fee_info[no].total_service_fee += _diff_service_fee;

    _curr_price_elec = s_fee_info[no].fee_strategy.period_elec[_curr_period];
    s_fee_info[no].price_elec_fee[_curr_price_elec] += _diff_elec_fee;
    _curr_price_service = s_fee_info[no].fee_strategy.period_service[_curr_period];
    s_fee_info[no].price_service_fee[_curr_price_service] += _diff_service_fee;

    if (_curr_price_elec == _curr_price_service) {
        s_fee_info[no].price_elect[_curr_price_elec] += _diff_elect; /* 只有当对应的时段中的电费价格与服务费价格用的相同时，这个值才有意义 */
    } else {
        s_fee_info[no].price_elect[_curr_price_elec] = 0xFF;
    }

    s_fee_info[no].period_elect[_curr_period] += _diff_elect;

#if (0x01 == FEE_STRATEGY_DEBUG)
    if (++count > 5) {
        count = 0;
        printf("[FSI] no: %d, p: %u, ef: %u, sf: %u, tt: %u, te: %u, tef: %u, tsf: %u\r\n", no,
                _curr_period, _curr_elec_fee, _curr_service_fee,
                s_fee_info[no].total_time, s_fee_info[no].total_elect, s_fee_info[no].total_elec_fee, s_fee_info[no].total_service_fee);
    }
#endif
}

/*****************************(C)COPYRIGHT(c) 2021 Thaisen *****END OF FILE****/
